package com.working.libview.widget.fish

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ObjectAnimator
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.MotionEvent
import android.widget.FrameLayout
import android.widget.ImageView
import androidx.annotation.Keep
import kotlin.math.acos
import kotlin.math.atan2
import kotlin.math.sqrt


class FishView : FrameLayout {
    private val fishDrawable by lazy { FishDrawable() }
    private val mPaint: Paint by lazy {
        Paint().apply {
            isAntiAlias = true
            isDither = true
            style = Paint.Style.STROKE
            strokeWidth = 8f
        }

    }
    private val mIvFish: ImageView by lazy {
        ImageView(context).apply {
            layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
            setImageDrawable(fishDrawable)
        }
    }

    private var touchX = 0f
    private var touchY = 0f

    private var ripple = 0f
        @Keep
        set(value) {
            alpha = (100 * (1 - value)).toInt()
            field = value
        }

    private var alpha = 0

    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    )

    constructor(
        context: Context,
        attrs: AttributeSet?,
        defStyleAttr: Int,
        defStyleRes: Int
    ) : super(context, attrs, defStyleAttr, defStyleRes)

    init {
        setWillNotDraw(false)
        addView(mIvFish)
    }


    private val rippleAnim: ObjectAnimator =
        ObjectAnimator.ofFloat(this, "ripple", 0f, 1f).setDuration(1000)

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        mPaint.alpha = alpha
        canvas?.drawCircle(touchX, touchY, ripple * 150, mPaint)
        invalidate()
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        touchX = event.x
        touchY = event.y

        mPaint.alpha = 100
        rippleAnim.start()

        drawSwingPath()
        return super.onTouchEvent(event)

    }

    private fun drawSwingPath() {
        //得到鱼的重心,相对坐标
        val middlePoint: PointF = fishDrawable.middlePoint
        //鱼重心的绝对坐标 fishMiddle = O
        val fishMiddle = PointF(mIvFish.x + middlePoint.x, mIvFish.y + middlePoint.y)
        //鱼头的绝对坐标 headControl = A
        val headControl = PointF(
            mIvFish.x + fishDrawable.headPoint.x,
            mIvFish.y + fishDrawable.headPoint.y
        )
        //结束点绝对坐标  endPoint = B
        val endPoint = PointF(touchX, touchY)

        //控制点2 设定为鱼头 重心 结束点夹角的一般
        // 求夹角公式：cosAOB = （OA * OB）/ |OA|*|OB|
        // OA = (Ax - Ox,Ay - Oy)
        // OB = (Bx - Ox,By - Oy)
        // OA * OB = (Ax - Ox)*(Bx - Ox) + (Ay - Oy)(By - Oy)
        // |OA| 表示OA的模
        // angle是AOB大角
        val angle = getAngle(fishMiddle, headControl, endPoint) / 2
        val delta = getAngle(fishMiddle, PointF(fishMiddle.x + 1, fishMiddle.y), headControl)
        val control2: PointF = fishDrawable.calculatePoint(
            fishMiddle,
            fishDrawable.headRadius * 1.6f,
            angle + delta
        )
        val path = Path()
        //此处要减去重心的距离，否者会抖一下
        path.moveTo(fishMiddle.x - middlePoint.x, fishMiddle.y - middlePoint.y)
        path.cubicTo(
            headControl.x - middlePoint.x, headControl.y - middlePoint.y,
            control2.x - middlePoint.x, control2.y - middlePoint.y,
            endPoint.x - middlePoint.x, endPoint.y - middlePoint.y
        )
        val objectAnimator = ObjectAnimator.ofFloat(mIvFish, "x", "y", path)
        objectAnimator.duration = 3000
        //监听动画开始和结束，调节摆尾频率
        objectAnimator.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator) {
                super.onAnimationEnd(animation)
                //在此处设置频率可能会产生抖动
                //fishDrawable.setFrequence(1f);
            }

            override fun onAnimationStart(animation: Animator) {
                super.onAnimationStart(animation)
                //                fishDrawable.setFrequence(4f);
            }
        })

        //获取path的切线值  使用pathMeasure来获取
        val pathMeasure = PathMeasure(path, false)
        //用于存放x y
        val tan = FloatArray(2)
        //获取路径上变化的x y
        objectAnimator.addUpdateListener { valueAnimator -> //animatedFraction是已经执行了的百分比
            val animatedFraction = valueAnimator.animatedFraction
            //此处设置会在斜率比较大的时候摆率比较高
            fishDrawable.frequence = 3f * (1 - animatedFraction) + 1
            //pathMeasure.getLength() * animatedFraction 已经经过了百分比
            //getPosTan获得路径上的tan值
            //通过该方法把值放入tan数组中
            pathMeasure.getPosTan(pathMeasure.length * animatedFraction, null, tan)
            //Y坐标是反的，所以用负号
            fishDrawable.originMainAngle = Math.toDegrees(
                atan2(
                    (-tan[1]).toDouble(),
                    tan[0].toDouble()
                )
            ).toFloat()
        }
        objectAnimator.start()
    }

    private fun getAngle(O: PointF, A: PointF, B: PointF): Float {
        val AOB = (A.x - O.x) * (B.x - O.x) + (A.y - O.y) * (B.y - O.y)
        val OALength =
            sqrt(((A.x - O.x) * (A.x - O.x) + (A.y - O.y) * (A.y - O.y)).toDouble()).toFloat()
        // OB 的长度
        val OBLength =
            sqrt(((B.x - O.x) * (B.x - O.x) + (B.y - O.y) * (B.y - O.y)).toDouble()).toFloat()
        val cosAOB = AOB / (OALength * OBLength)
        // 反余弦
        val angleAOB = Math.toDegrees(acos(cosAOB.toDouble())).toFloat()

        //AB与X轴的tan值 - OB与X轴的tan值
        val direction = (A.y - B.y) / (A.x - B.x) - (O.y - B.y) / (O.x - B.x)

        //点在鱼右边direction小于0  在左边 direction大于0
        return if (direction == 0f) {
            if (AOB >= 0) {
                0f
            } else {
                180f
            }
        } else {
            if (direction > 0) {
                -angleAOB
            } else {
                angleAOB
            }
        }
    }
}